import frappe
import requests
from datetime import datetime, timedelta
from matrix_fo.module.utils.cache_utils import cache_utils
from frappe.utils.user import get_user_fullname

def update_sales_order_qty_from_dn(doc, method=None):
    """
    根据提交的 Delivery Note, 更新对应销售订单的明细字段，包括：
    - 累加 custom_已出库数量
    - 更新 custom_可出库数量
    - 如果行状态为 NORMAL，则改为 PARTIAL_DELIVERY
    """

    # 确保当前单据状态正确
    if doc.docstatus != 1:
        return
    
    so_doc_map = {}

    for row in doc.items:
        so_no = row.against_sales_order
        so_detail_id = row.so_detail
        real_qty = round(float(row.qty or 0), 3)  # 注意用出库数量

        if not so_no or not so_detail_id:
            continue

        # 缓存销售订单
        if so_no not in so_doc_map:
            so_doc_map[so_no] = frappe.get_doc("Sales Order", so_no)
        so_doc = so_doc_map[so_no]

        # 找到销售订单明细行
        so_row = next((item for item in so_doc.items if item.name == so_detail_id), None)
        if not so_row:
            continue

        try:
            already_out = round(float(so_row.custom_已出库数量 or 0), 3)
        except (ValueError, TypeError):
            already_out = 0

        new_out = already_out + real_qty
        so_row.custom_已出库数量 = str(round(new_out, 3))

        # 计算可出库数量
        try:
            in_qty = float(so_row.custom_入库数量 or 0)
            so_row_qty = float(so_row.qty or 0)  # 新增：获取销售订单行总数量
        except (ValueError, TypeError):
            in_qty = 0
            so_row_qty = 0  # 新增：异常时默认0

        # 新增：按2%差距规则计算可出库数量
        if so_row_qty > 0:
            diff_percent = abs(in_qty - so_row_qty) / so_row_qty
            if diff_percent <= 0.02:
                remaining = so_row_qty - new_out
            else:
                remaining = in_qty - new_out
        else:
            remaining = in_qty - new_out
        # 保留原有：确保可出库数量不小于0
        remaining = max(0, remaining)
        so_row.custom_可出库数量 = str(round(remaining, 3))

        # 如果当前状态是 NORMAL，改为 PARTIAL_DELIVERY
        if so_row.custom_line_status == "NORMAL":
            so_row.custom_line_status = "PARTIAL_DELIVERY"

        # 如果已出库数量 >= 行数量，改为 CLOSE
        try:
            so_row_qty = float(so_row.qty or 0)
        except (ValueError, TypeError):
            so_row_qty = 0

        if new_out >= so_row_qty:
            so_row.custom_line_status = "CLOSE"
            so_row.custom_close_reason = "交货完成-自动关闭"
            so_row.custom_close_date = frappe.utils.now()
            so_row.custom_close_by = get_user_fullname(doc.owner)

            # 更新相关的销售出库单行状态为 CLOSE
            delivery_notes = frappe.get_all(
                "Delivery Note Item",
                filters={"so_detail": so_detail_id},  # 使用 Sales Order 明细行的 ID 进行匹配
                fields=["parent"]
            )
            for dn_item in delivery_notes:
                dn_doc = frappe.get_doc("Delivery Note", dn_item.parent)
                if dn_doc.docstatus == 2:
                    continue  # 已取消，跳过
                for dn_row in dn_doc.items:
                    if dn_row.so_detail == so_detail_id and dn_row.custom_line_status == "NORMAL":
                        dn_row.custom_line_status = "CLOSE"
                        dn_row.custom_close_reason = "交货完成-自动关闭"
                        dn_row.custom_close_date = frappe.utils.now()
                        dn_row.custom_close_by = get_user_fullname(doc.owner)
                        dn_row.save(ignore_permissions=True)

    # 保存所有被修改的销售订单
    for so_doc in so_doc_map.values():
        so_doc.flags.ignore_validate_update_after_submit = True
        so_doc.save(ignore_permissions=True)


def rollback_sales_order_qty_from_dn(doc, method=None):
    """
    取消 Delivery Note 时，回滚销售订单中的出库数量
    """
    so_doc_map = {}

    for row in doc.items:
        so_no = row.against_sales_order
        so_detail_id = row.so_detail
        real_qty = round(float(row.qty or 0), 3)

        if not so_no or not so_detail_id:
            continue

        # 缓存销售订单
        if so_no not in so_doc_map:
            so_doc_map[so_no] = frappe.get_doc("Sales Order", so_no)
        so_doc = so_doc_map[so_no]

        # 找到销售订单明细行
        so_row = next((item for item in so_doc.items if item.name == so_detail_id), None)
        if not so_row:
            continue

        try:
            already_out = round(float(so_row.custom_已出库数量 or 0), 3)
        except (ValueError, TypeError):
            already_out = 0

        new_out = max(0, already_out - real_qty)
        so_row.custom_已出库数量 = str(round(new_out, 3))

        # 计算新可出库数量
        try:
            in_qty = float(so_row.custom_入库数量 or 0)
            so_row_qty = float(so_row.qty or 0)  # 新增：获取销售订单行总数量
        except (ValueError, TypeError):
            in_qty = 0
            so_row_qty = 0  # 新增：异常时默认0

        # 新增：按2%差距规则计算可出库数量
        if so_row_qty > 0:
            diff_percent = abs(in_qty - so_row_qty) / so_row_qty
            if diff_percent <= 0.02:
                remaining = so_row_qty - new_out
            else:
                remaining = in_qty - new_out
        else:
            remaining = in_qty - new_out
        # 保留原有：确保可出库数量不小于0
        remaining = max(0, remaining)
        so_row.custom_可出库数量 = str(round(remaining, 3))

    for so_doc in so_doc_map.values():
        so_doc.flags.ignore_validate_update_after_submit = True
        so_doc.save(ignore_permissions=True)

@frappe.whitelist(allow_guest=False)
def update_ps_delivery_info(**kwargs):
    import json
    delivery_list = kwargs.get("DELIVERY_NOTE_LIST")

    if not delivery_list:
        return {"error": "DELIVERY_NOTE_LIST参数为空"}

    # if isinstance(payload, str):
    #     payload = json.loads(payload)

    updated_notes = []
    failed_notes = []

    # delivery_list = payload.get("DELIVERY_NOTE_LIST", [])
    for row in delivery_list:
        dn_name = row.get("DELIVERY_NOTE")
        if not dn_name:
            continue

        try:
            if frappe.db.exists("Delivery Note", dn_name):
                dn = frappe.get_doc("Delivery Note", dn_name)

                # 逐字段判断有值才更新
                if row.get("SP_STATUS"):
                    dn.set("custom_送货单状态", row.get("SP_STATUS"))

                if row.get("DELIVERY_PICTURE_LINK"):
                    dn.set("custom_送货图片链接", row.get("DELIVERY_PICTURE_LINK"))

                if row.get("RECEIVING_PICTURE_LINK"):
                    dn.set("custom_收货图片链接", row.get("RECEIVING_PICTURE_LINK"))

                dn.save(ignore_permissions=True)
                updated_notes.append(dn_name)
            else:
                failed_notes.append({"name": dn_name, "reason": "Not Found"})
        except Exception as e:
            failed_notes.append({"name": dn_name, "reason": str(e)})

    return {
        "updated": updated_notes,
        "failed": failed_notes
    }

def send_delivery_note_to_sp(doc, method):
    '''销售出库单保存时发送数据到SP系统, 创建打包任务'''
    if doc.get("custom_delivery_note_sp发送标识") not in [None, "", "未发送"]:
        return  # 已发送或有其他状态，不触发

    try:
        # 构造请求数据
        # 转换 posting_date 为 datetime 对象
        posting_date = datetime.strptime(str(doc.posting_date), "%Y-%m-%d")
        # 只保留年月日的字符串格式
        expect_delivery_time = posting_date.strftime("%Y-%m-%d")
        expect_back_time = (posting_date + timedelta(days=1)).strftime("%Y-%m-%d")
        payload = {
            "site": "1000",
            "deliveryNo": doc.name,
            "customNo": doc.customer,
            # "customNo": "YRJM001",
            "expectDeliveryTime":expect_delivery_time,
            "expectBackTime": expect_back_time,
            "detailList": []
        }

        for item in doc.items:
            payload["detailList"].append({
                "customItemNo": item.custom_客户料号,
                "itemSpecDesc": item.custom_规格,
                "platingType":  item.custom_客户镀种,
                "qty": item.qty
            })
        token = cache_utils.get_token()
        # frappe.throw(frappe.as_json(payload))
        # 记录日志
        # frappe.logger().info(f"[发送SP交货单] Payload: {frappe.as_json(payload)}")
        # 发送 POST 请求
        response = requests.post(
            "https://p.ss-matrix.com/api/psi/deliveryOrder/create",
            json=payload,
            headers={
                # "Authorization": "Bearer c336c903-11c3-4959-902c-df99d8f87503",
                "Authorization": "Bearer "+token,
                "Content-Type": "application/json"
            },
            timeout=10
        )
        # frappe.throw(token)
        # 检查响应
        if response.status_code != 200:
            doc.db_set("custom_delivery_note_sp发送标识", "失败")
            frappe.throw(f"发送交货单失败: {response.text}", "DeliveryNote POST Error")

        res_json = response.json()
        if not res_json.get("ok", False):
            doc.db_set("custom_delivery_note_sp发送标识", "失败")
            frappe.throw(
                f"""发送失败: 
                code={res_json.get("code")},
                msg={res_json.get("msg")},
                deliveryNo={payload['deliveryNo']}
                """,
                "DeliveryNote POST Business Error"
            )
        doc.db_set("custom_delivery_note_sp发送标识", "成功")
    
    except requests.exceptions.Timeout:
        frappe.throw("请求SP系统超时，请稍后重试。", "DeliveryNote Timeout Error")
    except Exception as e:
        frappe.throw(frappe.get_traceback(), "DeliveryNote Hook Exception")

@frappe.whitelist(allow_guest=False)
def close_delivery_note_lines(delivery_note, line_status, close_reason, idx_list):
    '''
    批量关闭销售出库单行，并同步关闭对应的销售订单行（如果存在）
    '''
    if isinstance(idx_list, str):
        import json
        idx_list = json.loads(idx_list)

    doc = frappe.get_doc("Delivery Note", delivery_note)
    user = frappe.get_doc("User", frappe.session.user)

    # 用于缓存已加载的销售订单，避免重复获取
    sales_order_map = {}

    for item in doc.items:
        if item.idx in idx_list:
            # 更新出库行
            item.custom_line_status = line_status
            item.custom_close_reason = close_reason
            item.custom_close_by = user.full_name
            item.custom_close_date = frappe.utils.now_datetime()

            # 如果有关联销售订单
            if item.against_sales_order and item.so_detail:
                so_name = item.against_sales_order

                # 如果还没加载对应销售订单，则加载并缓存
                if so_name not in sales_order_map:
                    sales_order_map[so_name] = frappe.get_doc("Sales Order", so_name)

                so_doc = sales_order_map[so_name]

                # 查找并更新对应销售订单行
                for so_item in so_doc.items:
                    if so_item.name == item.so_detail:
                        so_item.custom_line_status = line_status
                        so_item.custom_close_reason = close_reason
                        so_item.custom_close_by = user.full_name
                        so_item.custom_close_date = frappe.utils.now_datetime()
                        break

    # 保存出库单
    doc.save(ignore_permissions=True)

    # 保存所有被修改过的销售订单
    for so_doc in sales_order_map.values():
        so_doc.save(ignore_permissions=True)

    frappe.db.commit()
    return {"status": "success"}

